<template>
  <div class="ct-cell" ref="cellRef" :class="{error: !!errText}">
    <component
      v-if="computedType"
      :is="computedComponent"
      v-bind="computedBind"
      :modelValue="scope.row[column.prop]"
      @update:modelValue="handleUpdateValue"
    ></component>
    <span v-else>{{ computedText }}</span>
  </div>
  <div class="error-text" v-if="computedShowErr || errText"><div class="inner">{{ errText }}</div></div>
</template>
<script setup lang="ts">
import {computed, h, isVNode, onMounted, ref, nextTick, onUnmounted, watch, inject} from 'vue'
import enumCmp from './components'

type Props = {
  column: ColumnItem
  scope: {row:any, $index: number, column: any}
}

const props = withDefaults(defineProps<Props>(), {})


const valdateErrCache:Record<string, string> = inject('valdateErrCache', {})
const validates:Record<string, any> = inject('validates', {})
const clearValidates:Record<string, any> = inject('clearValidates', {})
const columnKey = props.scope.row._XID + '&#' + props.column.prop

const cellRef = ref<HTMLDivElement | null>(null)
const errText = ref('')

const setTooltipCfg:any = inject('setTooltipCfg')

const excuteProps = (prop:any) => {
  const {row, $index} = props.scope
  if (typeof prop === 'function') {
    return prop(row, $index)
  } else {
    return prop
  }
}

/**
 * 校验规则
 */
const excuteValidate = async () => {
  if (computedOptions.value.validator) {
    const {row, $index} = props.scope
    errText.value = await computedOptions.value.validator(row, $index)
    valdateErrCache[columnKey] = errText.value
    return errText.value
  }
  return ''
}

const clearValidate = () => {
  errText.value = ''
  valdateErrCache[columnKey] = ''
}
const handleUpdateValue = (val:any) => {
  // eslint-disable-next-line vue/no-mutating-props
  props.scope.row[props.column.prop] = val
  nextTick(() => {
    excuteValidate()
  })
}

const computedText = computed(() => {
  if (props.column.type === 'index') {
    return props.scope.$index + 1
  } else {
    return props.scope.row[props.column.prop]
  }
})

const computedOptions = computed(() => {
  return props.column.bodyOptions || ({} as any)
})

const computedShowErr = computed(() => {
  for (let key in valdateErrCache) {
    if (key.indexOf(props.scope.row._XID) > -1 && valdateErrCache[key]) {
      return true
    }
  }
  return false
})

/**
 * 组件类型
 */
const computedType = computed(() => {
  let type = excuteProps(computedOptions.value.type)
  if (!type && props.column.formatter) {
    type = '_formatter'
  }
  return type
})

const computedBind = computed(() => {
  if (isVNode(excuteProps(computedOptions.value.bind))) {
    return {}
  }
  return excuteProps(computedOptions.value.bind) || {}
})

const computedComponent = computed(() => {
  if (computedType.value === 'custom') {
    if (isVNode(excuteProps(computedOptions.value.bind))) {
      return excuteProps(computedOptions.value.bind)
    }
    return computedBind.value.component
  } else if (computedType.value === '_formatter') {
    return {
      render () {
        const result = props.column.formatter && props.column.formatter(props.scope.row, props.scope.column, props.scope.row[props.column.prop], props.scope.$index)
        if (typeof result === 'string') {
          return h('span', result)
        }
        return result
      }
    }
  } else {
    return enumCmp[computedType.value]
  }
})

const computedTooltip = computed(() => {
  if (props.column.tooltip) {
    return props.column.tooltip(props.scope.row, props.scope.$index)
  }
  return null
})

/**
 * tooltip处理方法
 */
const tooltipEvtOver = () => {
  setTooltipCfg({
    visible: true,
    text: computedTooltip.value,
    virtualRef: cellRef.value,
    placement: props.column.tooltipPlacement
  })
}

const tooltipEvtLeave = () => {
  setTooltipCfg({
    visible: false
  })
}

watch(() => computedTooltip.value, () => {
  nextTick(() => {
    addTooltipEvt()
  })
})

/**
 * 增加tooltip显示事件监听
 */
const addTooltipEvt = () => {
  if (cellRef.value) {
    removeTooltipEvt()
    if (computedTooltip.value) {
      cellRef.value.addEventListener('mouseover', tooltipEvtOver, false)
      cellRef.value.addEventListener('mouseleave', tooltipEvtLeave, false)
    }
  }
}
/**
 * 移除事件监听
 */
const removeTooltipEvt = () => {
  if (cellRef.value) {
    cellRef.value.removeEventListener('mouseover', tooltipEvtOver)
    cellRef.value.removeEventListener('mouseleave', tooltipEvtLeave)
  }
}


/**
 * 组件挂载渲染
 */
onMounted(() => {
  addTooltipEvt()
  if (computedOptions.value.validator) {
    validates[columnKey] = excuteValidate
    clearValidates[columnKey] = clearValidate
  }
})
/**
 * 组件卸载
 */
onUnmounted(() => {
  removeTooltipEvt()
  if (computedOptions.value.validator) {
    delete validates[columnKey]
    delete clearValidates[columnKey]
    delete valdateErrCache[columnKey]
  }
})

</script>
<style lang="scss" scoped>
.ct-cell {
  display: inline;
  &.error {
    :deep(.el-input__wrapper, .el-input__wrapper:hover) {
      box-shadow: 0 0 0 1px var(--el-color-danger) inset;
    }
  }
}
.error-text {
  color: var(--el-color-danger);
  font-size: 12px;
  line-height: 18px;
  height: 22px;
  padding-top: 4px;
  .inner {
    width: 95%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}
</style>